<template>
	<div class="processing-box">
		<div ref="processing" class="processing"></div>
	</div>
</template>

<script>
import * as THREE from 'three';
import { Detector } from '@/utils/detector';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass';
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader';

let camera = null; // 相机
let scene = null; // 场景
let renderer = null; // 渲染
let raycaster = new THREE.Raycaster(); // 光线投射 - 响应鼠标事件
let mouse = new THREE.Vector2(); // mouse
let controls = null;
// let state = new State(); // 帧率检测
let width = 0;
let height = 0;
let composer = null; // 后期处理组合器
// let renderPass = null;
let effectFXXAA = null;
let outlinePass = null;
let gui = null;
let params = null;

export default {
	name: 'Index',
	mounted() {
		this.init();
	},
	methods: {
		init() {
			if (!Detector.webgl) return Detector.addGetWebGLMessage(), alert('不支持webgl'), !1;
			width = this.$refs.processing.offsetWidth; // 获取显示宽度和高度
			height = this.$refs.processing.offsetHeight;
			scene = new THREE.Scene(); // 建立场景
			// 窗口变化
			window.addEventListener('resize', this.onWindowResize);
			this.initScene();
			this.initLight();
			this.initRenderer();
			this.initControls();
			this.initModel();
			this.initGUI();
			this.render();
		},
		// 构建模型
		initModel() {
			// 创建球体 - 颜色为随机数
			const geometry = new THREE.SphereGeometry(3, 48, 24);
			for (let i = 0; i < 20; i++) {
				const materal = new THREE.MeshLambertMaterial();
				materal.color.setHSL(Math.random(), 1.0, 0.3);
				const mesh = new THREE.Mesh(geometry, materal);
				mesh.position.set(Math.random() * 4 - 2, Math.random() * 4 - 2, Math.random() * 4 - 2);
				mesh.scale.multiplyScalar(Math.random() * 0.3 + 0.1);
				scene.add(mesh);
				mesh.receiveShadow = true;
				mesh.castShadow = true;
			}

			// // 创建地面
			// const floor = new THREE.Mesh(
			//   new THREE.PlaneGeometry(10, 10),
			//   new THREE.MeshLambertMaterial({side: THREE.DoubleSide})
			// )
			// floor.rotation.x = -Math.PI * 0.5
			// floor.position.y = -0.5
			// scene.add(floor)
			// floor.receiveShadow = true

			// outline - /该参数是WebGLRenderer对象
			composer = new EffectComposer(renderer);
			// 新建场景通道，如果不再建一个场景，什么也看不到
			let renderPass = new RenderPass(scene, camera);
			composer.addPass(renderPass);
			// 该通道可以为物体的边缘添加一个发光效果
			outlinePass = new OutlinePass(
				new THREE.Vector2(window.innerHeight, window.innerHeight),
				scene,
				camera
			);
			composer.addPass(outlinePass);

			// 设置贴图
			const textureLoader = new THREE.TextureLoader();
			let texture = textureLoader.load(require('@/assets/img/2.webp'));
			outlinePass.patternTexture = texture;
			texture.wrapS = texture.wrapT = THREE.RepeatWrapping;

			// 消除锯齿
			effectFXXAA = new ShaderPass(FXAAShader);
			effectFXXAA.uniforms['resolution'].value.set(1 / width, 1 / height);
			composer.addPass(effectFXXAA);

			// 鼠标移动
			renderer.domElement.style.touchAction = 'none';
			renderer.domElement.addEventListener('pointermove', this.onPointerMove);
		},
		// GUI控制器
		initGUI() {
			gui = new GUI();
			// 定义参数
			params = {
				edgeStrenth: 10.0, // 边缘的强度
				edgeGlow: 0.5,
				edgeThickness: 5.0, // 粗细
				pulsePeriod: 0.5, // 闪烁周期
				usePatternTexture: false, // 是否使用纹理
				visibleEdgeColor: '#ff0000', // 可见的颜色
				hiddenEdgeColor: '#0000ff' // 被遮挡的颜色
			};
			this.updateOutline();

			gui.add(params, 'edgeStrenth', 0.01, 10).onChange(() => this.updateOutline());
			gui.add(params, 'edgeGlow', 0, 1).onChange(() => this.updateOutline());
			gui.add(params, 'edgeThickness', 0.1, 10).onChange(() => this.updateOutline());
			gui.add(params, 'pulsePeriod', 0, 10).onChange(() => this.updateOutline());
			gui.add(params, 'usePatternTexture').onChange(() => this.updateOutline());
			gui.addColor(params, 'visibleEdgeColor').onChange(() => this.updateOutline());
			gui.addColor(params, 'hiddenEdgeColor').onChange(() => this.updateOutline());
		},
		// 更新视图
		updateOutline() {
			// 将值弄到outline里面去
			outlinePass.edgeStrenth = params.edgeStrenth;
			outlinePass.edgeGlow = params.edgeGlow;
			outlinePass.edgeThickness = params.edgeThickness;
			outlinePass.usePatternTexture = params.usePatternTexture;
			outlinePass.visibleEdgeColor.set(params.visibleEdgeColor);
			outlinePass.hiddenEdgeColor.set(params.hiddenEdgeColor);
		},
		// 鼠标移动
		onPointerMove(event) {
			if (event.isPrimary === false) return;
			mouse.x = (event.x / width) * 2 - 1;
			mouse.y = -(event.y / height) * 2 + 1;
		},
		// 窗口变化
		onWindowResize() {
			// 相机设置
			width = this.$refs.processing.offsetWidth; // 获取显示宽度和高度
			height = this.$refs.processing.offsetHeight;
			let k = width / height; //窗口宽高比
			camera.aspect = k;
			camera.updateProjectionMatrix();
			renderer.setSize(width, height);
			composer.setSize(width, height);
			effectFXXAA.uniforms['resolution'].value.set(1 / width, 1 / height);
		},
		// 光线
		initLight() {
			const d = 400;
			// 环境光
			let light = new THREE.AmbientLight(0xffffff);
			scene.add(light);
			// 平行光
			let directionalLight = new THREE.DirectionalLight('#fff', 0.5);
			directionalLight.position.set(20, 120, 20);
			directionalLight.castShadow = true;
			directionalLight.shadow.camera.near = 10;
			directionalLight.shadow.camera.far = 1000;
			directionalLight.shadow.mapSize.width = 1024;
			directionalLight.shadow.mapSize.height = 1024;
			directionalLight.shadow.camera.top = d; // 显示位置大小
			directionalLight.shadow.camera.right = d;
			directionalLight.shadow.camera.left = -d;
			directionalLight.shadow.camera.bottom = -d;
			directionalLight.castShadow = true; // 支持投影
			directionalLight.shadow.radius = 3; // 调整阴影模糊程度，值越大越模糊
			scene.add(directionalLight);
		},
		// 相机
		initScene() {
			// 相机设置
			let k = width / height; //窗口宽高比
			let s = 4; //三维场景显示范围控制系数，系数越大，显示的范围越大，看到的东西越小
			//创建相机对象（透视相机） - 相机可以缩放显示三维场景、对三维场景的不同角度进行取景显示。
			camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
			camera.position.set(10, 10, 10); //设置相机位置
			camera.lookAt(0, 0, 0); //设置相机方向(指向的场景对象)
		},
		// 渲染器
		initRenderer() {
			renderer = new THREE.WebGLRenderer({
				// alpha: true,
				antialias: true
				// logarithmicDepthBuffer: true,
			});
			renderer.setSize(width, height); //设置渲染区域尺寸
			renderer.setClearColor('#000', 1); //设置背景颜色
			renderer.setClearAlpha(1); // 背景透明度
			renderer.render(scene, camera);
			renderer.clear(); // 此方法将颜色缓存初始化为当前颜色
			renderer.shadowMap.enabled = true; // 可以渲染阴影
			renderer.getPixelRatio(Math.min(window.devicePixelRatio, 2)); // 设置像素比
			renderer.outputEncoding = THREE.sRGBEncoding;
			this.$refs.processing.appendChild(renderer.domElement);
			// document.body.append(state.domElement);
		},
		// 鼠标控制 - controls
		initControls() {
			// 控制器 放大倍数  缩小倍数  是否自动旋转及速度
			controls = new OrbitControls(camera, renderer.domElement);
			controls.target.set(0, 0, 0);
			// controls.autoRotate = true;
			// controls.enableDamping = true // 阻尼
			// controls.dampingFactor = 0.5 // 阻尼系数大小控制
			// controls.enablePan = true // 是否开启右键拖拽功能
			// controls.enableZoom = true // 是否可以缩放
			// controls.autoRotate = true // 是否自动旋转
			// controls.autoRotateSpeed = 0.3 // 旋转速度控制
			// controls.minDistance = 1; // 相机距离原点的最近距离
			// controls.maxDistance = 400; // 相机距离原点的最远距离
		},
		// 动画
		render() {
			// 获取时间 - 逐渐增加的时间
			// const elapsed = clock.getElapsedTime();
			// renderer.render(scene, camera); // 整体渲染
			controls.update();
			composer.render();
			raycaster.setFromCamera(mouse, camera);
			// 计算物体和射线的焦点 - intersectObject
			const intersects = raycaster.intersectObject(scene, true);
			if (intersects.length > 0) {
				const selectedObject = intersects[0].object;
				outlinePass.selectedObjects = [];
				outlinePass.selectedObjects.push(selectedObject);
			} else {
				outlinePass.selectedObjects = [];
			}
			requestAnimationFrame(this.render); //请求再次执行渲染函数render
		}
	}
};
</script>

<style lang="scss" scoped>
.processing-box {
	width: 100%;
	height: 100%;
	.processing {
		width: 100%;
		height: 100%;
	}
}
</style>
